require('dotenv').config();
const { NacosConfigClient } = require('nacos');
const fs = require('fs').promises;
const path = require('path');
const configPath = './editable/client.config.js';
const configList = require(configPath).default;
const namespace = path.basename(configPath, '.config.js').toUpperCase();

/**
 * 读取JSON文件内容
 * @param {string} filePath - 文件路径
 * @returns {Promise<object>} JSON对象
 */
async function readJsonFile(filePath) {
  try {
    const content = await fs.readFile(filePath, 'utf8');
    return JSON.parse(content);
  } catch (error) {
    if (error.code === 'ENOENT') {
      return null;
    }
    throw error;
  }
}

/**
 * 递归删除目录
 * @param {string} dirPath - 目录路径
 */
async function removeDirectory(dirPath) {
  try {
    const entries = await fs.readdir(dirPath, { withFileTypes: true });
    
    await Promise.all(entries.map(async (entry) => {
      const fullPath = path.join(dirPath, entry.name);
      return entry.isDirectory() ? 
        removeDirectory(fullPath) : 
        fs.unlink(fullPath);
    }));

    await fs.rmdir(dirPath);
  } catch (error) {
    if (error.code !== 'ENOENT') {
      throw error;
    }
  }
}

/**
 * 比较配置差异并生成差异报告
 * @param {string} dataId - 配置的DataId
 * @param {string} group - 配置的Group
 * @param {object} oldData - 旧配置数据
 * @param {object} newData - 新配置数据
 * @returns {string} Markdown格式的差异报告
 */
function generateDiffReport(dataId, group, oldData, newData) {
  if (!oldData) {
    return `### ${group} - ${dataId}\n- 新增配置\n\n`;
  }

  const differences = [];
  const oldKeys = Object.keys(oldData);
  const newKeys = Object.keys(newData);

  // 检查删除的键
  oldKeys.forEach(key => {
    if (!newKeys.includes(key)) {
      differences.push(`  - 删除: \`${key}\``);
    }
  });

  // 检查新增和修改的键
  newKeys.forEach(key => {
    if (!oldKeys.includes(key)) {
      differences.push(`  - 新增: \`${key}\` = ${JSON.stringify(newData[key])}`);
    } else if (JSON.stringify(oldData[key]) !== JSON.stringify(newData[key])) {
      differences.push(`  - 修改: \`${key}\`
    - 旧值: ${JSON.stringify(oldData[key])}
    - 新值: ${JSON.stringify(newData[key])}`);
    }
  });

  if (differences.length === 0) {
    return '';
  }

  return `### ${group} - ${dataId}\n${differences.join('\n')}\n\n`;
}

// 从环境变量获取 Nacos 配置
const getNacosConfig = () => ({
  serverAddr: process.env.NACOS_SERVER_ADDR,     // Nacos 地址
  namespace: namespace,                          // 命名空间 ID（从配置文件名提取）
  username: process.env.NACOS_USERNAME,         // 用户名
  password: process.env.NACOS_PASSWORD,         // 密码
  requestTimeout: parseInt(process.env.NACOS_REQUEST_TIMEOUT || '10000'), // 请求超时时间
  endpoint: process.env.NACOS_ENDPOINT || '',   // endpoint
  secure: process.env.NACOS_SECURE === 'true'   // 是否使用HTTPS
});

// 初始化 Nacos 客户端
const client = new NacosConfigClient(getNacosConfig());

// 本地缓存，避免频繁请求 Nacos
let cacheConfig = {};

/**
 * 测试 Nacos API
 */
async function testNacosAPI() {
  console.log('========= 测试 getConfigs() API =========');
  try {
    const configs = await nacosClient.getConfigs();
    console.log('getConfigs() 返回数据:', JSON.stringify(configs, null, 2));
    console.log('返回数据类型:', typeof configs);
    if (Array.isArray(configs)) {
      console.log('配置数量:', configs.length);
      
      // 如果有配置项，测试获取第一个配置的详细信息
      if (configs.length > 0) {
        const firstConfig = configs[0];
        console.log('\n========= 测试 getConfig() API =========');
        console.log('获取第一个配置详情:', firstConfig);
        const content = await nacosClient.getConfig(firstConfig.dataId, firstConfig.group);
        console.log(`配置 [${firstConfig.dataId}][${firstConfig.group}] 内容:`, content);
        try {
          const jsonContent = JSON.parse(content);
          console.log('解析为 JSON 后:', JSON.stringify(jsonContent, null, 2));
        } catch (e) {
          console.log('配置内容不是 JSON 格式');
        }
      }
    } else {
      console.log('getConfigs() 返回的不是数组，而是:', Object.prototype.toString.call(configs));
    }
  } catch (error) {
    console.error('API 测试失败:', error);
  }
  process.exit(0);
}

/**
 * 获取指定DataId和Group的配置列表
 * @returns {Promise<Array<{dataId: string, group: string}>>} 配置列表
 */
async function getConfigs() {
  return configList || [];
}

/**
 * 获取配置信息
 * @param {Array<{dataId: string, group: string}>} configList - DataId和Group的列表
 * @returns {Promise<Object<string, Object>>} 配置信息的映射，key为"dataId_group"
 */
async function fetchConfigurations(configList) {
  const configMap = {};
  
  for (const config of configList) {
    const { dataId, group } = config;
    const key = `${dataId}_${group}`;
    try {
      const content = await client.getConfig(dataId, group);
      configMap[key] = JSON.parse(content || '{}');
    } catch (err) {
      console.error(`获取配置失败 [${dataId}][${group}]:`, err);
      configMap[key] = {};
    }
  }
  
  return configMap;
}

/**
 * 确保目录存在，如果不存在则创建
 * @param {string} dirPath - 目录路径
 */
async function ensureDirectory(dirPath) {
  try {
    await fs.access(dirPath);
  } catch {
    await fs.mkdir(dirPath, { recursive: true });
  }
}

/**
 * 保存配置到JSON文件
 * @param {string} namespace - 命名空间
 * @param {string} dataId - 配置的DataId
 * @param {string} group - 配置的Group
 * @param {object} data - 配置数据
 */
async function saveConfigAsJson(namespace, dataId, group, data) {
  const jsonDirPath = path.join(__dirname, 'constants', 'namespace', namespace, 'json', group);
  await ensureDirectory(jsonDirPath);
  
  const jsonFilePath = path.join(jsonDirPath, `${dataId}.json`);
  await fs.writeFile(jsonFilePath, JSON.stringify(data, null, 2), 'utf8');
  console.log(`JSON file saved: ${jsonFilePath}`);
}

/**
 * 生成TypeScript文件内容
 * @param {string} dataId - 配置的DataId
 * @param {object} data - 配置数据
 * @returns {string} TypeScript文件内容
 */
function generateTypeScriptContent(dataId, data) {
  return `// Generated automatically by cloud-parking-config-service
// 请勿手动编辑或更新！

export const ${dataId} = ${JSON.stringify(data, null, 2)} as const;

export type ${dataId}Type = typeof ${dataId};
`;
}

/**
 * 生成JavaScript文件内容
 * @param {string} dataId - 配置的DataId
 * @param {object} data - 配置数据
 * @returns {string} JavaScript文件内容
 */
function generateJavaScriptContent(dataId, data) {
  return `// Generated automatically by cloud-parking-config-service
// Do not edit manually

exports.${dataId} = ${JSON.stringify(data, null, 2)};
`;
}

/**
 * 生成TypeScript和JavaScript文件
 * @param {string} dataId - 配置的DataId
 * @param {string} group - 配置的Group
 * @param {object} data - 配置数据
 */
async function generateConfigFiles(namespace, dataId, group, data) {
  const commonDirPath = path.join(__dirname, 'constants', 'namespace', namespace, 'common', group);
  await ensureDirectory(commonDirPath);

  // 生成 TypeScript 文件
  const tsContent = generateTypeScriptContent(dataId, data);
  const tsFilePath = path.join(commonDirPath, `${dataId}.ts`);
  await fs.writeFile(tsFilePath, tsContent, 'utf8');
  console.log(`TypeScript file saved: ${tsFilePath}`);

  // 生成 JavaScript 文件
  const jsContent = generateJavaScriptContent(dataId, data);
  const jsFilePath = path.join(commonDirPath, `${dataId}.js`);
  await fs.writeFile(jsFilePath, jsContent, 'utf8');
  console.log(`JavaScript file saved: ${jsFilePath}`);
}

/**
 * 获取旧的配置数据
 * @param {string} dataId - 配置的DataId
 * @param {string} group - 配置的Group
 * @returns {Promise<object>} 旧的配置数据
 */
async function getOldConfig(dataId, group) {
  const jsonFilePath = path.join(__dirname, 'constants', 'json', group, `${dataId}.json`);
  return await readJsonFile(jsonFilePath);
}

/**
 * 清理所有生成的文件和目录
 */
async function cleanGeneratedFiles() {
  const constantsDir = path.join(__dirname, 'constants');
  
  // 要清理的目录列表
  const dirsToClean = ['json', 'common'];
  
  for (const dir of dirsToClean) {
    await removeDirectory(path.join(constantsDir, dir));
  }
}

/**
 * 生成命名空间配置
 * @param {string} namespace - 命名空间
 * @param {Array<{dataId: string, group: string}>} configs - 配置列表
 * @returns {string} 配置文件内容
 */
function generateNamespaceConfig(namespace, configs) {
  const content = `// Generated automatically by cloud-parking-config-service
// 请勿手动更改!!!

exports.default = ${JSON.stringify(configs, null, 2)};
`;
  return content;
}

/**
 * 处理配置同步
 * @param {Array<{dataId: string, group: string}>} configs - 配置列表
 * @returns {string} 差异报告
 */
async function processConfigs(configs) {
  let diffReport = '';
  const configMap = await fetchConfigurations(configs);
  
  // 生成并保存配置文件
  const configContent = generateNamespaceConfig(namespace, configs);
  const configDir = path.join(__dirname, 'constants');
  await ensureDirectory(configDir);
  await fs.writeFile(
    path.join(configDir, 'config.js'),
    configContent,
    'utf8'
  );

  // 处理每个配置项
  for (const config of configs) {
    const { dataId, group } = config;
    const key = `${dataId}_${group}`;
    const newData = configMap[key];
    const oldData = await getOldConfig(dataId, group);
    
    const diff = generateDiffReport(dataId, group, oldData, newData);
    if (diff) {
      diffReport += diff;
    }

    // 保存配置文件
    await saveConfigAsJson(namespace, dataId, group, newData);
    await generateConfigFiles(namespace, dataId, group, newData);
  }

  return diffReport;
}

/**
 * 主函数：同步配置到本地
 */
async function syncConfigsToLocal() {
  try {
    console.log('开始同步配置到本地...');
    
    // 获取配置列表
    const configs = await getConfigs();
    console.log(`发现 ${configs.length} 个配置项`);

    // 清理旧文件
    console.log('清理旧文件...');
    await cleanGeneratedFiles();

    // 生成差异报告
    let diffReport = '# 配置变更报告\n' +
                    `生成时间: ${new Date().toLocaleString()}\n\n`;
    
    if (configs.length > 0) {
      const configReport = await processConfigs(configs);
      if (configReport) {
        diffReport += configReport;
      }
    }
    
    // 如果有差异，保存差异报告
    if (diffReport.split('\n').length > 3) {
      const diffDirPath = path.join(__dirname, 'constants');
      await ensureDirectory(diffDirPath);
      await fs.writeFile(
        path.join(diffDirPath, 'diff.md'),
        diffReport,
        'utf8'
      );
      console.log('\n差异报告已生成');
    } else {
      console.log('\n没有发现配置变更');
    }

    console.log('\n配置同步完成！');
    process.exit(0);
  } catch (error) {
    console.error('配置同步失败:', error);
    process.exit(1);
  }
}



syncConfigsToLocal();
